home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / GUSI 1.4.1 / GUSI / GUSITCP.cp < prev    next >
Encoding:
Text File  |  1994-05-04  |  22.6 KB  |  909 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSITCP.cp        -    TCP Stream Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by
  7.  
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.  
  11. Language    :    MPW C/C++
  12.  
  13. $Log: GUSITCP.cp,v $
  14. Revision 1.3  1994/05/04  01:47:24  neeri
  15. Long writes on nonblocking sockets would fail.
  16.  
  17. Revision 1.2  1994/05/01  23:48:59  neeri
  18. Enable recvfrom with non-null from address.
  19. Try to behave better when other side closes connection.
  20.  
  21. Revision 1.1  1994/02/25  02:30:45  neeri
  22. Initial revision
  23.  
  24. Revision 0.6  1993/08/25  00:00:00  neeri
  25. return correct peer address from accept()
  26.  
  27. Revision 0.5  1993/01/31  00:00:00  neeri
  28. Support for inetd
  29.  
  30. Revision 0.4  1993/01/21  00:00:00  neeri
  31. Simplify and correct code
  32.  
  33. Revision 0.3  1993/01/17  00:00:00  neeri
  34. Be more careful about user interrupts.
  35.  
  36. Revision 0.2  1993/01/08  00:00:00  neeri
  37. tcp_notify was setting the wrong state to unconnected
  38.  
  39. Revision 0.1  1992/09/08  00:00:00  neeri
  40. A big part should work now                
  41.  
  42. *********************************************************************/
  43.  
  44. #include "GUSIINET_P.h"
  45.  
  46. /********************** Completion procedures ***********************/
  47.  
  48. #pragma segment GUSIResident
  49.  
  50. pascal void tcp_notify(
  51.     StreamPtr,
  52.     u_short                    eventCode,
  53.     Ptr                        userDataPtr,
  54.     u_short,
  55.     struct ICMPReport *)
  56. {
  57.     TCPSocket *    sock    =    *(TCPSocket **) userDataPtr;
  58.  
  59.     switch (eventCode) {
  60.     case TCPClosing:
  61.         sock->sstate = SOCK_STATE_CLOSING;
  62.         break;
  63.  
  64.     case TCPTerminate:
  65.         sock->sstate = SOCK_STATE_UNCONNECTED;
  66.         break;
  67.     }
  68. }
  69.  
  70. void tcp_connect_done(AnnotatedPB *pb)
  71. {
  72.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  73.     TCPiopb *    tcp    =    pb->TCP();
  74.  
  75.     if (!tcp->ioResult) {
  76.         sock->sa.sin_addr.s_addr     = tcp->csParam.open.localHost;
  77.         sock->sa.sin_port             = tcp->csParam.open.localPort;
  78.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  79.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  80.         sock->sstate                     = SOCK_STATE_CONNECTED;
  81.         sock->asyncerr                 = noErr;
  82.     }
  83. }
  84.  
  85. void tcp_listen_done(AnnotatedPB *pb)
  86. {
  87.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  88.     TCPiopb *    tcp    =    pb->TCP();
  89.  
  90.     switch(tcp->ioResult) {
  91.     case noErr:
  92.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  93.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  94.         sock->sstate                     = SOCK_STATE_LIS_CON;
  95.         sock->asyncerr                 = 0;
  96.         break;
  97.  
  98.     case openFailed:
  99.     case invalidStreamPtr:
  100.     case connectionExists:
  101.     case duplicateSocket:
  102.     case commandTimeout:
  103.     default:
  104.         sock->sstate                     = SOCK_STATE_UNCONNECTED;
  105.         sock->asyncerr                 = tcp->ioResult;
  106.         break;
  107.     }
  108. }
  109.  
  110. void tcp_recv_done(AnnotatedPB *pb)
  111. {
  112.     TCPSocket *        sock    =    (TCPSocket *) pb->Owner();
  113.     TCPiopb *        tcp    =    pb->TCP();
  114.     register    int     readin;
  115.  
  116.     if (!tcp->ioResult) {
  117.         readin = tcp->csParam.receive.rcvBuffLen;
  118.         sock->recvd   = readin;
  119.     }
  120. }
  121.  
  122. void tcp_send_done(AnnotatedPB *pb)
  123. {
  124.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  125.     TCPiopb *    tcp    =    pb->TCP();
  126.  
  127.     switch (tcp->ioResult) {
  128.     case noErr:
  129.         ((wdsEntry *)(tcp->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  130.         break;
  131.  
  132.     case ipNoFragMemErr:
  133.     case connectionClosing:
  134.     case connectionTerminated:
  135.     case connectionDoesntExist:
  136.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  137.         sock->asyncerr = ENOTCONN;
  138.         break;
  139.  
  140.     case ipDontFragErr:
  141.     case invalidStreamPtr:
  142.     case invalidLength:
  143.     case invalidWDS:
  144.     default:
  145.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  146.         sock->asyncerr = tcp->ioResult;
  147.         break;
  148.     }
  149. }
  150.  
  151. #pragma segment GUSIINET
  152.  
  153. /************************ TCPSocket members *************************/
  154.  
  155.  
  156. TCPSocket::TCPSocket()
  157.     :    INETSocket()
  158. {
  159.     TCPiopb    pb;
  160.  
  161.     sstate     = SOCK_STATE_UNCONNECTED;
  162.     self          = new(TCPSocket *);
  163.     *self        = this;
  164.  
  165.     pb.ioCRefNum                         = INETSockets.Driver();
  166.     pb.csCode                             = TCPCreate;
  167.     pb.csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  168.     pb.csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  169.     pb.csParam.create.notifyProc     = tcp_notify;
  170.     pb.csParam.create.userDataPtr    = Ptr(self);
  171.  
  172.     switch(PBControlSync(ParmBlkPtr(&pb)))
  173.     {
  174.         case noErr:                     break;
  175.         case invalidLength:             GUSI_error(ENOBUFS);     return;
  176.         case invalidBufPtr:             GUSI_error(ENOBUFS);     return;
  177.         case insufficientResources:     GUSI_error(EMFILE);         return;
  178.         default:                             GUSI_error(ENETDOWN);     return;
  179.     }
  180.  
  181.     peer.sin_family         = AF_INET;
  182.     peer.sin_addr.s_addr = 0;
  183.     peer.sin_port             = 0;
  184.  
  185.     bzero(&peer.sin_zero[0], 8);
  186.  
  187.     asyncerr                 = 0;
  188.     stream                     = pb.tcpStream;
  189. }
  190.  
  191. TCPSocket::TCPSocket(StreamPtr stream)
  192.     :    INETSocket(stream)
  193. {
  194.     AppleEvent                theEvent, myReply;
  195.     AEDesc                    theAddress;
  196.     long                        theType = 'inet';
  197.     ProcPtr                    theProc    = (ProcPtr) tcp_notify;
  198.     ProcessSerialNumber    PSN;
  199.  
  200.     self          = new(TCPSocket *);
  201.     *self        = this;
  202.     asyncerr = 0;
  203.  
  204.     GetCurrentProcess(&PSN);
  205.     AECreateDesc(typeApplSignature, (Ptr) &theType, sizeof(theType), &theAddress);
  206.     AECreateAppleEvent('INET', 'TNFY',  &theAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  207.     
  208.     AEPutParamPtr(&theEvent, 'STRM', typeLongInteger, (Ptr) &stream, sizeof(stream));
  209.     AEPutParamPtr(&theEvent, 'ASR ', typeLongInteger, (Ptr) &theProc, sizeof(ProcPtr));
  210.     AEPutParamPtr(&theEvent, 'USRP', typeLongInteger, (Ptr) &self, sizeof(long));
  211.     AEPutParamPtr(&theEvent, keyProcessSerialNumber, typeProcessSerialNumber, (Ptr) &PSN, sizeof(ProcessSerialNumber));
  212.     
  213.     AESend(&theEvent, &myReply, kAEWaitReply, kAEHighPriority, 120, nil, nil);
  214.  
  215.     AEDisposeDesc(&myReply);
  216.     AEDisposeDesc(&theEvent);
  217.     AEDisposeDesc(&theAddress);
  218.  
  219.     TCPiopb * pb;
  220.  
  221.     pb                = GetPB();
  222.     pb->csCode     = TCPStatus;
  223.  
  224.     PBControlSync(ParmBlkPtr(pb));
  225.     
  226.     sa.sin_addr.s_addr     =     pb->csParam.status.localHost;
  227.     sa.sin_port                =    pb->csParam.status.localPort;
  228.     peer.sin_addr.s_addr =     pb->csParam.status.remoteHost;
  229.     peer.sin_port            =    pb->csParam.status.remotePort;
  230. }
  231.  
  232. TCPSocket::TCPSocket(TCPSocket * sock)
  233. {
  234.     stream        = sock->stream;
  235.     status        = sock->status;
  236.     nonblocking    = sock->nonblocking;
  237.     recvBuf        = sock->recvBuf;
  238.     recvd            = sock->recvd;
  239.     sa                = sock->sa;
  240.     peer            = sock->peer;
  241.     sstate        = sock->sstate;
  242.     asyncerr        = 0;
  243.     
  244.     // The reason for this strange code is that stream->userData points to
  245.     // sock.self and cannot be changed while the stream is alive
  246.     
  247.     self            = sock->self;
  248.     *self            = this;
  249.     sock->self    = new(TCPSocket *);
  250.     *sock->self    = sock;
  251. }
  252.  
  253. TCPSocket::~TCPSocket()
  254. {
  255.     TCPiopb *    pb;
  256.  
  257.     do {
  258.         pb                    = GetPB();
  259.         pb->csCode         = TCPStatus;
  260.  
  261.         PBControlSync(ParmBlkPtr(pb));
  262.  
  263.         SAFESPIN(false, SP_MISC, 0);
  264.     } while (!errno && pb->csParam.status.amtUnackedData > 0);
  265.  
  266.     pb                                                = GetPB();
  267.     pb->ioCompletion                            = nil;
  268.     pb->csCode                                     = TCPClose;
  269.     pb->csParam.close.validityFlags         = timeoutValue | timeoutAction;
  270.     pb->csParam.close.ulpTimeoutValue     = 60 /* seconds */;
  271.     pb->csParam.close.ulpTimeoutAction     = 1 /* 1:abort 0:report */;
  272.  
  273.     switch (PBControlAsync(ParmBlkPtr(pb)))
  274.     {
  275.         case noErr:
  276.         case connectionClosing:
  277.             break;
  278.         case connectionDoesntExist:
  279.         case connectionTerminated:
  280.             break;
  281.         case invalidStreamPtr:
  282.         default:
  283.             return;
  284.     }
  285.  
  286.     {
  287.         rdsEntry    rdsarray[TCP_MAX_WDS+1];
  288.         int        passcount;
  289.         const int maxpass =4;
  290.  
  291.         pb                    =    GetPB();
  292.  
  293.         for (passcount=0; passcount<maxpass; passcount++) {
  294.             pb->csCode                                             = TCPNoCopyRcv;
  295.             pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  296.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  297.             pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  298.  
  299.             if (PBControlSync(ParmBlkPtr(pb)))
  300.                 break;
  301.  
  302.             pb->csCode                                             = TCPRcvBfrReturn;
  303.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  304.  
  305.             PBControlSync(ParmBlkPtr(pb));
  306.  
  307.             SAFESPIN(false, SP_MISC, 0);
  308.             
  309.             if (errno)
  310.                 break;
  311.         }
  312.  
  313.         if (passcount == maxpass) {        /* remote side isn't being nice */
  314.             /* then try again */
  315.  
  316.             PBControlSync(ParmBlkPtr(pb));
  317.  
  318.             for (passcount=0; passcount<maxpass; passcount++) {
  319.                 pb->csCode                                             = TCPNoCopyRcv;
  320.                 pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  321.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  322.                 pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  323.  
  324.                 if (PBControlSync(ParmBlkPtr(pb)))
  325.                     break;
  326.  
  327.                 pb->csCode                                             = TCPRcvBfrReturn;
  328.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  329.  
  330.                 PBControlSync(ParmBlkPtr(pb));
  331.  
  332.                 SAFESPIN(false, SP_MISC, 0);
  333.                 
  334.                 if (errno)
  335.                     break;
  336.             }
  337.         }
  338.     }
  339.  
  340.     /* destroy the stream */
  341.     pb->csCode     = TCPRelease;
  342.  
  343.     if (PBControlSync(ParmBlkPtr(pb)))
  344.         return;
  345.  
  346.     DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  347.     
  348.     delete self;
  349. }
  350.  
  351. TCPiopb * TCPSocket::GetPB()
  352. {
  353.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  354.     pb->TCP()->ioCRefNum =    INETSockets.Driver();
  355.     pb->TCP()->tcpStream    =    stream;
  356.  
  357.     pb->SetOwner(this);
  358.  
  359.     return pb->TCP();
  360. }
  361.  
  362. u_long TCPSocket::Available()
  363. {
  364.     TCPiopb * pb;
  365.  
  366.     pb                = GetPB();
  367.     pb->csCode     = TCPStatus;
  368.  
  369.     PBControlSync(ParmBlkPtr(pb));
  370.  
  371.     return pb->csParam.status.amtUnreadData;
  372. }
  373.  
  374. /*
  375.  *    connect - initiate a connection on a MacTCP socket
  376.  *
  377.  *        This call attempts to make a
  378.  *        connection to another socket. The other socket is specified
  379.  *        by an internet address and port.
  380.  *
  381.  *        TCP sockets may successfully connect() only once;
  382.  *
  383.  *        If the connection or binding succeeds, then 0 is returned.
  384.  *        Otherwise a -1 is returned, and a more specific error code
  385.  *        is stored in errno.
  386.  *
  387.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  388.  *
  389.  *        EHOSTUNREACH        The TCP connection came up half-way and
  390.  *                          then failed.
  391.  */
  392.  
  393. int TCPSocket::connect(void * address, int addrlen)
  394. {
  395.     OSErr                        err;
  396.     struct sockaddr_in *    addr    =    (struct sockaddr_in *) address;
  397.     TCPiopb    *                pb;
  398.  
  399.     if (addrlen != sizeof(struct sockaddr_in))
  400.         return GUSI_error(EINVAL);
  401.  
  402.     if (addr->sin_family != AF_INET)
  403.         return GUSI_error(EAFNOSUPPORT);
  404.  
  405.     /* Make sure this socket can connect. */
  406.     if (sstate == SOCK_STATE_CONNECTING)
  407.         return GUSI_error(EALREADY);
  408.     if (sstate != SOCK_STATE_UNCONNECTED)
  409.         return GUSI_error(EISCONN);
  410.  
  411.     sstate = SOCK_STATE_CONNECTING;
  412.  
  413.     pb                                                    = GetPB();
  414.     pb->ioCompletion                                = TCPIOCompletionProc(tcp_connect_done);
  415.     pb->csCode                                         = TCPActiveOpen;
  416.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  417.     pb->csParam.open.ulpTimeoutValue         = 60 /* seconds */;
  418.     pb->csParam.open.ulpTimeoutAction         = 1 /* 1:abort 0:report */;
  419.     pb->csParam.open.commandTimeoutValue     = 0;
  420.     pb->csParam.open.remoteHost                 = addr->sin_addr.s_addr;
  421.     pb->csParam.open.remotePort                 = addr->sin_port;
  422.     pb->csParam.open.localHost                 = 0;
  423.     pb->csParam.open.localPort                 = sa.sin_port;
  424.     pb->csParam.open.dontFrag                     = 0;
  425.     pb->csParam.open.timeToLive                 = 0;
  426.     pb->csParam.open.security                     = 0;
  427.     pb->csParam.open.optionCnt                 = 0;
  428.  
  429.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  430.     {
  431.         sstate = SOCK_STATE_UNCONNECTED;
  432.         return TCP_error(err);
  433.     }
  434.  
  435.     if (nonblocking)
  436.         return GUSI_error(EINPROGRESS);
  437.  
  438.     /* sync connect - spin till TCPActiveOpen completes */
  439.  
  440.     SAFESPIN(pb->ioResult==inProgress, SP_MISC, 0);
  441.  
  442.     if (errno || pb->ioResult) {
  443.         sstate = SOCK_STATE_UNCONNECTED;
  444.         
  445.         if (errno)
  446.             return -1;
  447.         else
  448.             return TCP_error(pb->ioResult);
  449.     } else
  450.         return 0;
  451. }
  452.  
  453. int TCPSocket::listen(int)
  454. {
  455.     OSErr            err;
  456.     TCPiopb *    pb;
  457.  
  458.     if (sstate != SOCK_STATE_UNCONNECTED)
  459.         return GUSI_error(EISCONN);
  460.  
  461.     sstate                                             = SOCK_STATE_LISTENING;
  462.     pb                                                    = GetPB();
  463.     pb->ioCRefNum                                     = INETSockets.Driver();
  464.     pb->ioCompletion                                = TCPIOCompletionProc(tcp_listen_done);
  465.     pb->csCode                                         = TCPPassiveOpen;
  466.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  467.     pb->csParam.open.ulpTimeoutValue         = 255 /* seconds */;
  468.     pb->csParam.open.ulpTimeoutAction         = 0 /* 1:abort 0:report */;
  469.     pb->csParam.open.commandTimeoutValue     = 0 /* infinity */;
  470.     pb->csParam.open.remoteHost                 = 0;
  471.     pb->csParam.open.remotePort                 = 0;
  472.     pb->csParam.open.localHost                 = 0;
  473.     pb->csParam.open.localPort                 = sa.sin_port;
  474.     pb->csParam.open.dontFrag                     = 0;
  475.     pb->csParam.open.timeToLive                 = 0;
  476.     pb->csParam.open.security                     = 0;
  477.     pb->csParam.open.optionCnt                 = 0;
  478.  
  479.     if (err = PBControlAsync(ParmBlkPtr(pb))) {
  480.         sstate = SOCK_STATE_UNCONNECTED;
  481.         
  482.         return TCP_error(err);
  483.     }
  484.     
  485.     SAFESPIN(!pb->csParam.open.localPort, SP_MISC, 0);
  486.         
  487.     if (errno) {
  488.         sstate = SOCK_STATE_UNCONNECTED;
  489.         
  490.         return -1;
  491.     }
  492.  
  493.     sa.sin_addr.s_addr     = pb->csParam.open.localHost;
  494.     sa.sin_port             = pb->csParam.open.localPort;
  495.  
  496.     return 0;
  497. }
  498.  
  499. Socket * TCPSocket::accept(void *from, int *fromlen)
  500. {
  501.     TCPSocket *        sock;
  502.     TCPiopb *        pb;
  503.  
  504.     if (sstate == SOCK_STATE_UNCONNECTED)
  505.         if (asyncerr) {
  506.             TCP_error(asyncerr);
  507.             asyncerr = 0;
  508.  
  509.             return nil;
  510.         } else
  511.             return (Socket *) GUSI_error_nil(ENOTCONN);
  512.  
  513.     if (sstate != SOCK_STATE_LISTENING && sstate != SOCK_STATE_LIS_CON)
  514.         return (Socket *) GUSI_error_nil(ENOTCONN);
  515.  
  516.     if (sstate == SOCK_STATE_LISTENING) {
  517.         if (nonblocking)
  518.             return (Socket *) GUSI_error_nil(EWOULDBLOCK);
  519.  
  520.         /*    Spin till sock_tcp_listen_done runs. */
  521.         SPINP(sstate == SOCK_STATE_LISTENING, SP_MISC, 0);
  522.  
  523.         /* got notification - was it success? */
  524.         if (sstate != SOCK_STATE_LIS_CON) {
  525.             (void) TCP_error(asyncerr);
  526.             asyncerr = 0;
  527.             return nil;
  528.         }
  529.     }
  530.  
  531.     /*
  532.      * Have connection.  Duplicate this socket.  The client gets the connection
  533.      * on the new socket and I create a new stream on the old socket and put it
  534.      * in listen state.
  535.      */
  536.     sstate     = SOCK_STATE_CONNECTED;
  537.     sock        = new TCPSocket(this);
  538.  
  539.     if (!sock)
  540.     {
  541.         /*    Abort the incoming connection. */
  542.         pb                 = GetPB();
  543.         pb->csCode         = TCPAbort;
  544.  
  545.         PBControlSync(ParmBlkPtr(pb));
  546.  
  547.         sstate = SOCK_STATE_UNCONNECTED;
  548.  
  549.         /* try and put the socket back in listen mode */
  550.         if (listen(5) < 0)
  551.         {
  552.             sstate = SOCK_STATE_UNCONNECTED;
  553.             return nil;        /* errno already set */
  554.         }
  555.         return (Socket *) GUSI_error_nil(ENOMEM);
  556.     }
  557.  
  558.     /* Create a new MacTCP stream on the old socket and put it into */
  559.     /* listen state to accept more connections. */
  560.     sstate = SOCK_STATE_UNCONNECTED;
  561.  
  562.     pb                                            = GetPB();
  563.     pb->csCode                                 = TCPCreate;
  564.     pb->csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  565.     pb->csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  566.     pb->csParam.create.notifyProc     = tcp_notify;
  567.     pb->csParam.create.userDataPtr    = Ptr(self);
  568.  
  569.     switch(PBControlSync(ParmBlkPtr(pb)))
  570.     {
  571.         case noErr:                     break;
  572.         case invalidLength:             return (Socket *) GUSI_error_nil(ENOBUFS);
  573.         case invalidBufPtr:             return (Socket *) GUSI_error_nil(ENOBUFS);
  574.         case insufficientResources:     return (Socket *) GUSI_error_nil(EMFILE);
  575.         default:                             return (Socket *) GUSI_error_nil(ENETDOWN);
  576.     }
  577.  
  578.     peer.sin_family         = AF_INET;
  579.     peer.sin_addr.s_addr = 0;
  580.     peer.sin_port             = 0;
  581.  
  582.     bzero(&peer.sin_zero[0], 8);
  583.  
  584.     asyncerr                 = 0;
  585.     stream                     = pb->tcpStream;
  586.  
  587.     if (listen(5) < 0) {
  588.         /* nothing to listen on */
  589.         sstate = SOCK_STATE_UNCONNECTED;
  590.  
  591.         /* kill the incoming connection */
  592.         pb                    = sock->GetPB();
  593.         pb->csCode        = TCPRelease;
  594.  
  595.         if (!PBControlSync(ParmBlkPtr(pb)))
  596.             DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  597.  
  598.         return nil; /* errno set */
  599.     }
  600.  
  601.     /* return address of partner */
  602.     memcpy(from, &sock->peer, *fromlen = min(*fromlen, sizeof(struct sockaddr_in)));
  603.  
  604.     return sock;
  605. }
  606.  
  607. /*
  608.  *    TCPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  609.  *
  610.  *        recvfrom() attempts to receive a message (ie a datagram)
  611.  *        on the socket s.
  612.  *
  613.  *        from returns the address of the socket which sent the message.
  614.  *        fromlen is the usual value-result length parameter.
  615.  *
  616.  *        Typically, read() is used with a TCP stream and recv() with
  617.  *        UDP where the idea of a message makes more sense. But in fact,
  618.  *        read() and recv() are equivalent.
  619.  *
  620.  *        Regardless of non-blocking status, if less data is available
  621.  *        than has been requested, only that much data is returned.
  622.  *
  623.  *        If the socket is marked for non-blocking I/O, and the socket
  624.  *        is empty, the operation will fail with the error EWOULDBLOCK.
  625.  *        Otherwise, the operation will block until data is available
  626.  *        or an error occurs.
  627.  *
  628.  *        A return value of zero indicates that the stream has been
  629.  *        closed and all data has already been read. ie. end-of-file.
  630.  *
  631.  *        Flags is ignored.
  632.  *
  633.  *        If successful, the number of bytes actually received is
  634.  *        returned. Otherwise, a -1 is returned and the global variable
  635.  *        errno is set to indicate the error.
  636.  *
  637.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  638.  */
  639.  
  640. int TCPSocket::recvfrom(void * buffer, int buflen, int, void * from, int * fromlen)
  641. {
  642.     TCPiopb    *    pb;
  643.     u_long        dataavail;
  644.  
  645.     if (from)
  646.         getpeername(from, fromlen);
  647.     if (status & SOCK_STATUS_NOREAD)
  648.         return GUSI_error(ESHUTDOWN);
  649.  
  650.     /* socket hasn't finished connecting yet */
  651.     if (sstate == SOCK_STATE_CONNECTING)
  652.     {
  653.         if (nonblocking)
  654.             return GUSI_error(EWOULDBLOCK);
  655.  
  656.         /* async connect and sync recv? */
  657.  
  658.         SPIN(sstate == SOCK_STATE_CONNECTING,SP_MISC,0);
  659.     }
  660.  
  661.     /* socket is not connected */
  662.     if (!(sstate == SOCK_STATE_CONNECTED))
  663.     {
  664.         /* see if the connect died (pretty poor test) */
  665.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0 && asyncerr != 1)
  666.         {
  667.             (void) TCP_error(asyncerr);
  668.             asyncerr = 0;
  669.             return -1;
  670.         }
  671.  
  672.         /* I guess he just forgot */
  673.         return GUSI_error(ENOTCONN);
  674.     }
  675.  
  676.     dataavail = Available();
  677.  
  678.     if (nonblocking && !dataavail)
  679.         return GUSI_error(EWOULDBLOCK);
  680.         
  681.     recvBuf    = (char *) buffer;
  682.     recvd        = 0;
  683.     asyncerr    = inProgress;
  684.  
  685.     pb                                                     = GetPB();
  686.     pb->ioCompletion                                    = TCPIOCompletionProc(tcp_recv_done);
  687.     pb->csCode                                             = TCPRcv;
  688.     pb->csParam.receive.commandTimeoutValue     = 0; /* seconds, 0 = blocking */
  689.     pb->csParam.receive.rcvBuff                     = recvBuf;
  690.     pb->csParam.receive.rcvBuffLen                 = min(buflen,TCP_MAX_MSG);
  691.  
  692.     PBControlAsync(ParmBlkPtr(pb));
  693.  
  694.     /* This is potentially dangerous, as there doesn't seem to be a way to
  695.         stop the receive call on an user abort.
  696.     */
  697.     SPIN(pb->ioResult==inProgress, SP_STREAM_READ, buflen);
  698.  
  699.     if (pb->ioResult == commandTimeout)
  700.         pb->ioResult = noErr;
  701.  
  702.     switch(pb->ioResult)
  703.     {
  704.         case noErr:
  705.             asyncerr = noErr;
  706.  
  707.             return recvd;
  708.  
  709.         case connectionClosing:
  710.         case connectionTerminated:
  711.             return recvd;
  712.  
  713.         case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  714.         case connectionDoesntExist:
  715.         case invalidStreamPtr:
  716.         case invalidLength:
  717.         case invalidBufPtr:
  718.         default:
  719.             return TCP_error(pb->ioResult);
  720.     }
  721. }
  722.  
  723. /*
  724.  *    TCPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  725.  *
  726.  *        sendto() is used to transmit a message to another
  727.  *        socket on the socket s.
  728.  *
  729.  *        Typically, write() is used with a TCP stream and send() with
  730.  *        UDP where the idea of a message makes more sense. But in fact,
  731.  *        write() and send() are equivalent.
  732.  *
  733.  *        Write() and send() operations are not considered complete
  734.  *        until all data has been sent and acknowledged.
  735.  *
  736.  *        If a socket is marked for non-blocking I/O, the operation
  737.  *        will return an 'error' of EINPROGRESS.
  738.  *
  739.  *        If the socket is not marked for non-blocking I/O, the write will
  740.  *        block until space becomes available.
  741.  *
  742.  *        write() and send() may be used only when the socket is in a connected
  743.  *        state, sendto() may be used at any time.
  744.  *
  745.  *        Flags is ignored.
  746.  *
  747.  *        These calls return the number of bytes sent, or -1 if an error
  748.  *        occurred.
  749.  *
  750.  *        EINVAL              The sum of the iov_len values in the iov array was
  751.  *                                greater than 65535 (TCP) or 65507 (UDP) or there
  752.  *                      were too many entries in the array (16 for TCP or
  753.  *                      6 for UDP).
  754.  *
  755.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  756.  *
  757.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  758.  *
  759.  *        ENOBUFS          The transmit queue is full. (UDP)
  760.  */
  761.  
  762. int TCPSocket::sendto(void * buffer, int count, int flags, void * to, int)
  763. {
  764.     int            bytes,towrite;
  765.     miniwds *    thiswds;
  766.     short            wdsnum;
  767.     TCPiopb *    pb;
  768.     miniwds        wdsarray[TCP_MAX_WDS];
  769.  
  770.     if (status & SOCK_STATUS_NOWRITE)
  771.         return GUSI_error(ESHUTDOWN);
  772.  
  773.     if (to != NULL) /* sendto */
  774.         return GUSI_error(EOPNOTSUPP);
  775.     if (sstate != SOCK_STATE_CONNECTED && sstate != SOCK_STATE_CONNECTING)
  776.         return GUSI_error(ENOTCONN);
  777.  
  778.     /* socket hasn't finished connecting yet */
  779.     if (sstate == SOCK_STATE_CONNECTING) {
  780.         if (nonblocking)
  781.             return GUSI_error(EALREADY);
  782.  
  783.         /* async connect and sync send? */
  784.         SPIN(sstate == SOCK_STATE_CONNECTING, SP_MISC, 0);
  785.     }
  786.  
  787.     /* socket is not connected */
  788.     if (!(sstate == SOCK_STATE_CONNECTED)) {
  789.         /* see if a previous operation failed */
  790.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0) {
  791.             (void) TCP_error(asyncerr);
  792.             asyncerr = 0;
  793.             return -1;
  794.         }
  795.  
  796.         /* I guess he just forgot */
  797.         return GUSI_error(ENOTCONN);
  798.     }
  799.  
  800.     pb                    = GetPB();
  801.     pb->csCode         = TCPStatus;
  802.  
  803.     if (PBControlSync(ParmBlkPtr(pb)))
  804.         bytes = 0;
  805.     else {
  806.         bytes = pb->csParam.status.sendWindow - pb->csParam.status.amtUnackedData;
  807.  
  808.         if (bytes < 0)
  809.             bytes = 0;
  810.     }
  811.  
  812.     if (nonblocking)
  813.         if (!bytes)
  814.             return GUSI_error(EWOULDBLOCK);
  815.         else if (bytes < count)
  816.             count = bytes;
  817.  
  818.     bytes    =    count;                                                /* save count before we nuke it */
  819.     memset(wdsarray, 0, TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  820.     thiswds = wdsarray;
  821.     wdsnum = 0;
  822.  
  823.     while (count > 0) {
  824.         /* make sure the thing that just finished worked ok */
  825.         if (asyncerr) {
  826.             (void) GUSI_error(asyncerr);
  827.             asyncerr = 0;
  828.             return -1;
  829.         }
  830.  
  831.         towrite=min(count,TCP_MAX_MSG);
  832.  
  833.         /* find a clean wds */
  834.  
  835.         while (thiswds->length != 0) {
  836.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  837.             if (wdsnum)
  838.                 thiswds++;
  839.             else
  840.                 thiswds = wdsarray;
  841.             SPIN(false, SP_STREAM_WRITE, count);    /* spin once */
  842.         }
  843.  
  844.         /* find a clean pb */
  845.  
  846.         thiswds->length                            = (short)towrite;
  847.         thiswds->ptr                                = (char *) buffer;
  848.         pb                                                = GetPB();
  849.         pb->ioCompletion                            = TCPIOCompletionProc(tcp_send_done);
  850.         pb->csCode                                     = TCPSend;
  851.         pb->csParam.send.validityFlags         = timeoutValue | timeoutAction;
  852.         pb->csParam.send.ulpTimeoutValue     = 60 /* seconds */;
  853.         pb->csParam.send.ulpTimeoutAction     = 1 /* 0:abort 1:report */;
  854.         pb->csParam.send.pushFlag                 = count <= TCP_MAX_MSG;
  855.         pb->csParam.send.urgentFlag             = flags & MSG_OOB;
  856.         pb->csParam.send.wdsPtr                 = (Ptr)thiswds;
  857.         pb->csParam.send.sendFree                 = 0;
  858.         pb->csParam.send.sendLength             = 0;
  859.  
  860.         PBControlAsync(ParmBlkPtr(pb));
  861.  
  862.         SPIN(false, SP_STREAM_WRITE, count);
  863.         count     -= towrite;
  864.         buffer    = (char *) buffer + towrite;
  865.     }
  866.  
  867.     SPIN(pb->ioResult == inProgress, SP_STREAM_WRITE, 0);
  868.  
  869.     if (!pb->ioResult)
  870.         return(bytes);
  871.     else
  872.         return TCP_error(pb->ioResult);
  873. }
  874.  
  875. int TCPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  876. {
  877.     int    goodies     =     0;
  878.  
  879.     if (canRead)
  880.         switch (sstate) {
  881.         case SOCK_STATE_LIS_CON:
  882.             *canRead    = true;
  883.             ++goodies;
  884.             break;
  885.         case SOCK_STATE_CONNECTED:
  886.             if (Available()) {
  887.                 *canRead    = true;
  888.                 ++goodies;
  889.             }
  890.             break;
  891.         case SOCK_STATE_UNCONNECTED:
  892.         case SOCK_STATE_CLOSING:
  893.             *canRead    = true;
  894.             ++goodies;
  895.             break;
  896.         }
  897.  
  898.     if (canWrite)
  899.         switch (sstate) {
  900.         case SOCK_STATE_CONNECTING:
  901.             break;
  902.         default:
  903.             *canWrite = true;
  904.             ++goodies;
  905.         }
  906.  
  907.     return goodies;
  908. }
  909.